package com.k12.file.utils;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.write.metadata.WriteSheet;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

public class ExcelUtil {

    /**
     * 通用导出方法（适用于百万级数据）
     * @param response HttpServletResponse 响应对象
     * @param fileName 导出文件名（不需要后缀）
     * @param sheetName sheet名称
     * @param headClass 表头对应的Class对象
     * @param <T> 数据类型
     */
    public static <T> void export(HttpServletResponse response,
                                         String fileName,
                                         String sheetName,
                                         Class<T> headClass,
                                         DataSupplier<T> dataSupplier) throws IOException {
        // 设置响应内容类型和编码
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8).replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");

        // 使用 try-with-resources 自动关闭 ExcelWriter
        try (ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream(), headClass).build()) {
            WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).build();

            // 分批次查询和写入
            int page = 1;
            int pageSize = 10000; // 每批处理1万条数据
            List<T> data;

            while (!(data = dataSupplier.getData(page, pageSize)).isEmpty()) {
                excelWriter.write(data, writeSheet);
                page++;
            }
        }
    }




    /**
     * 通用导入方法
     * @param inputStream Excel文件输入流
     * @param headClass 表头对应的Class对象
     * @param consumer 数据处理消费者
     * @param <T> 数据类型
     */
    public static <T> void importExcel(InputStream inputStream,
                                       Class<T> headClass,
                                       Consumer<T> consumer) {
        // 读取Excel文件
        EasyExcel.read(inputStream, headClass, new ReadListener<T>() {
            // 批处理阈值：每100条处理一次
            private static final int BATCH_COUNT = 100;
            // 临时存储数据
            private final List<T> cachedDataList = new ArrayList<>(BATCH_COUNT);

            @Override
            public void invoke(T data, AnalysisContext context) {
                // 将数据添加到缓存列表
                cachedDataList.add(data);
                // 达到BATCH_COUNT时，处理一次数据
                if (cachedDataList.size() >= BATCH_COUNT) {
                    processData();
                    // 处理完成后清理列表
                    cachedDataList.clear();
                }
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {
                // 最后一批不足BATCH_COUNT的数据也要处理
                if (!cachedDataList.isEmpty()) {
                    processData();
                    cachedDataList.clear();
                }
            }

            // 处理数据的方法
            private void processData() {
                for (T data : cachedDataList) {
                    consumer.accept(data);
                }
            }
        }).sheet().doRead();
    }

    // 数据提供者接口（用于分页查询数据）
    public interface DataSupplier<T> {
        List<T> getData(int page, int pageSize);
    }

}